import 'dart:io';

import 'package:cookie_jar/cookie_jar.dart';
import 'package:dio/adapter.dart';
import 'package:dio/dio.dart';
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

typedef HttpSuccessCallBack<T> = void Function(dynamic data);
typedef HttpFailCallBack = void Function(DioError err);

/// 网络请求管理
/// 1. HttpManager 设置为单利模式，便于管理网络请求
class HttpManager {
  /// 设置 cookie 管理
  CookieJar _cookieJar;

  /// 用于取消发起的请求
  CancelToken _cancelToken;

  Dio _dio;

  // 创建单利
  static final HttpManager _instance = HttpManager._internal();

  factory HttpManager() => _instance;
  HttpManager._internal() {
    if (_cancelToken == null) _cancelToken = CancelToken();

    if (_cookieJar == null) _cookieJar = CookieJar();

    if (_dio == null) {
      BaseOptions options = BaseOptions(
        connectTimeout: HttpDefaultOption.CON_TIMEOUT,
        receiveTimeout: HttpDefaultOption.RECIVE_TIMEOUT,
        sendTimeout: HttpDefaultOption.SEND_TIMEOUT,
        responseType: ResponseType.json, // 响应数据的类型`json`
      );

      _dio = Dio(options);
    }
  }

  /// 初始化
  /// 这里的 `options` 配置会覆盖掉 `HttpManager._internal()` 中的 配置。
  void configration({
    String baseUrl,
    int connectTimeout,
    int receiveTimeout,
    int sendTimeout,
    Map<String, dynamic> extra,
    Map<String, dynamic> headers,
    ResponseType responseType,
    String contentType,
    ValidateStatus validateStatus,
    bool receiveDataWhenStatusError,
    bool followRedirects,
    int maxRedirects,
    RequestEncoder requestEncoder,
    ResponseDecoder responseDecoder,
    List<Interceptor> interceptors,
    bool proxy = false,
    String proxyHostAndPort,
    bool isOpenCookie = true,
  }) {
    // 初始化配置更改
    _dio.options = _dio.options.merge(
      baseUrl: baseUrl,
      connectTimeout: connectTimeout,
      receiveTimeout: receiveTimeout,
      sendTimeout: sendTimeout,
      extra: extra,
      headers: headers,
      responseType: responseType,
      contentType: contentType,
      validateStatus: validateStatus,
      receiveDataWhenStatusError: receiveDataWhenStatusError,
      followRedirects: followRedirects,
      maxRedirects: maxRedirects,
      requestEncoder: requestEncoder,
      responseDecoder: responseDecoder,
    );

    // 添加拦截器
    if (interceptors != null && interceptors.isNotEmpty) {
      _dio.interceptors.addAll(interceptors);
    }

    // 添加Http接口拦截
    if (proxy && proxyHostAndPort != null && proxyHostAndPort.isNotEmpty) {
      httpProxy(proxyHostAndPort);
    }

    // cookies管理
    if (isOpenCookie) {
      cookieManager();
    }
  }

  /// HTTP 代理
  void httpProxy(String proxyHostAndPort) {
    (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
        (client) {
      client.findProxy = (uri) {
        return "PROXY $proxyHostAndPort";
      };

      //代理工具会提供一个抓包的自签名证书，会通不过证书校验，所以我们禁用证书校验
      client.badCertificateCallback =
          (X509Certificate cert, String host, int port) => true;
    };
  }

  /// 添加cookie管理
  void cookieManager() {
    _dio.interceptors.add(CookieManager(_cookieJar));
  }

  /// 取消所有网络请求
  void cancelAll({dynamic error}) {
    if (!_cancelToken.isCancelled) {
      _cancelToken.cancel(error);
    }
  }

  /// 启动网络请求，为了解决 调用 `cancelAll` 方法以后，所有的`token`都全局生效导致以后的请求都会被取消
  void enableRequest() {
    _cancelToken = null;
    _cancelToken = CancelToken();
  }

  /// 请求网络接口
  void requestBlock({
    @required String path,
    String method = HttpMethod.GET,
    data,
    Map<String, dynamic> queryParameters,
    Options options,
    HttpSuccessCallBack successCallBack,
    HttpFailCallBack failCallBack,
  }) async {
    // 参数校验
    options?.method = method;
    options = options ?? Options(method: method);

    try {
      Response response = await _dio.request(
        path,
        data: data,
        queryParameters: queryParameters,
        options: options,
        cancelToken: _cancelToken,
      );

      if (successCallBack != null) successCallBack(response.data);
    } catch (e) {
      if (failCallBack != null) failCallBack(e);
    }
  }

  /// 请求网络接口
  Future<dynamic> requestAsync({
    @required String path,
    String method = HttpMethod.GET,
    data,
    Map<String, dynamic> queryParameters,
    Options options,
  }) async {
    // 参数校验
    options?.method = method;
    options = options ?? Options(method: method);

    try {
      Response response = await _dio.request(
        path,
        data: data,
        queryParameters: queryParameters,
        options: options,
        cancelToken: _cancelToken,
      );

      return response.data;
    } catch (e) {
      return Future.error(e);
    }
  }
}

/// 请求方式
class HttpMethod {
  static const String GET = "get";
  static const String POST = "post";
}

/// `Option` 默认配置
class HttpDefaultOption {
  static const int CON_TIMEOUT = 1000;
  static const int RECIVE_TIMEOUT = 1000;
  static const int SEND_TIMEOUT = 1000;
}